package com.hippomeetsskunk.savanna.types.person;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.apache.log4j.Logger;

import com.hippomeetsskunk.savanna.types.meta.PersonType;
import com.hippomeetsskunk.savanna.types.meta.ResourceType;
import com.hippomeetsskunk.savanna.types.meta.SimObjectType;
import com.hippomeetsskunk.savanna.types.person.Attributes.Attribute;
import com.hippomeetsskunk.savanna.types.resource.Holder;
import com.hippomeetsskunk.savanna.types.resource.RelocatableObject;
import com.hippomeetsskunk.savanna.types.resource.SimObject;
import com.hippomeetsskunk.savanna.types.world.Location;
import com.hippomeetsskunk.savanna.types.world.Region;
import com.hippomeetsskunk.savanna.types.world.Time;

public class Person implements Holder {
	
	private static final Logger logger = Logger.getLogger(Person.class);
	
	private PersonType type;
	final private String name;
	final private Set<SimObject> assigned; // not necessary the owner!
	// do not store goals on person (better for rule engine)
	// final private Set<Goal> goals;
	private Location location;
	final private Skills skills;
	final private Attributes attributes;
	final private Nourishment nourishment;
	
	private double gold;
	
	public Person(String name, PersonType type){
		this.type = type;
		this.name = name;
		this.assigned = new HashSet<SimObject>();
		// this.goals = new HashSet<Goal>();
		this.skills = new Skills();
		this.attributes = new Attributes();
		this.nourishment = new Nourishment();
	}
	
	public String getName() {
		return name;
	}

	public void assign(SimObject object){
		assigned.add(object);
	}
	
	public void withdraw(SimObject object){
		assigned.remove(object);
	}
	
	public Collection<SimObject> getAssigned(){
		return assigned;
	}
	
//	public void addGoal(Goal goal){
//		this.goals.add(goal);
//		logger.debug(this + " adding goal " + goal);
//	}
//	
//	public void removeGoal(Goal goal){
//		this.goals.remove(goal);
//		logger.debug(this + " removing goal " + goal);
//	}
//	
//	public Collection<Goal> getGoals(){
//		return goals;
//	}
	
//	public Goal getMostUrgentGoal(){
//		Goal u = null;
//		for(Goal g : goals){
//			if (u == null || g.getUrgency() > u.getUrgency()) u = g;
//		}
//		return u;
//	}

	public Location getLocation() {
		return location;
	}

	public void setLocation(Location location) {
		this.location = location;
	}

	public Skills getSkills() {
		return skills;
	}

	public Attributes getAttributes() {
		return attributes;
	}

	public boolean startDay(Time time, Region region) {
		nourishment.digest(this);
		logger.debug(this + " health = " + attributes.get(Attribute.HEALTH));
		return attributes.get(Attribute.HEALTH) > 0.0;
	}

	public Nourishment getNourishment() {
		return nourishment;
	}

	@Override
	public String toString() {
		return getName() + "(" + getType() + ")";
	}

	public Set<ResourceType> getMissingResources(Set<ResourceType> types){
		if (types == null) return Collections.emptySet();
		Set<ResourceType> missingTypes = new HashSet<ResourceType>();
		for(ResourceType t : types){
			SimObject o = getResource(t);
			if (o == null) missingTypes.add(t);
		}
		return missingTypes;
	}

	private SimObject getResource(SimObjectType type) {
		for(SimObject a : assigned){
			if (a.getType().isA(type)) return a;
		}
		return null;
	}

	public List<RelocatableObject> getResources(Set<ResourceType> types) {
		List<RelocatableObject> resources = new ArrayList<RelocatableObject>();
		for(SimObjectType t : types){
			SimObject object = getResource(t);
			if (object instanceof RelocatableObject){
				resources.add((RelocatableObject) object);
			}
			else{
				throw new IllegalArgumentException("Wrong resource type or resource not available");
			}
		}
		return resources;
	}

	public PersonType getType() {
		return type;
	}

	public void setType(PersonType type) {
		this.type = type;
	}
	
	public double getGold(){
		return gold;
	}
	
	public void addGold(double gold){
		this.gold += gold;
	}
	
	// maybe couple with object (i.e. buy(object, price))
	public void removeGold(double gold){
		this.gold -= gold;
	}
}
